/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package managedbean.MrpAtp;

import merlion.mrp.ppm.pojo.LongHolder;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import managedbean.Workspace.JobManagerBean;
import merlion.common.util.Consts;
import merlion.mrp.atpm.entity.PendingATP;
import merlion.mrp.atpm.session.ATPSessionBeanLocal;
import merlion.scm.icm.session.InventoryControlSessionBeanLocal;

/**
 *
 * @author Zhang Ying
 */
@ManagedBean
@ViewScoped
public class ATPManagerBean {

    @EJB
    private ATPSessionBeanLocal atpSession;
    @EJB
    private InventoryControlSessionBeanLocal invSession;
    @ManagedProperty(value = "#{jobManagerBean}")
    private JobManagerBean jobManager;
    private ArrayList<Integer> counterForProduct;
    private ArrayList<Integer> counterForWeek;
    // simu for rfq  or so
    private Calendar requestedDate;
    private Calendar requestedDateSO;
    private ArrayList<Integer> numOfDays;// size: n
    private ArrayList<Calendar> changedDates;  // size : n
    private ArrayList<ArrayList<LongHolder>> dailyDemands;  // size: 4*n
    private ArrayList<ArrayList<Long>> origDailyDemands;  // 4*n
    private ArrayList<ArrayList<LongHolder>> otDailyDemands;  // 4*n
    private ArrayList<ArrayList<Long>> origOtDailyDemands;// 4*n
    private PendingATP pendingATP;
    private Timestamp indicativeLeadtime;
    // simu for rfq
    private PendingATP pendingRFQ;
    // simu for so
    private PendingATP pendingSO;

    /** Creates a new instance of ATPManagerBean */
    public ATPManagerBean() {
        counterForProduct = new ArrayList<Integer>();
        for (int i = 0; i < 4; i++) {
            counterForProduct.add(i);
        }
        counterForWeek = new ArrayList<Integer>();
        for (int i = 0; i < 5; i++) {
            counterForWeek.add(i);
        }
    }

    public Timestamp getIndicativeLeadtime() {
        return indicativeLeadtime;
    }

    public String returnIndicativeLeadTime() {
        if (getIndicativeLeadtime() == null) {
            return "";
        }
        return Consts.sdfDmy.format(getIndicativeLeadtime());
    }

    public void setIndicativeLeadtime(Timestamp indicativeLeadtime) {
        this.indicativeLeadtime = indicativeLeadtime;
    }

    public Long returnCurrentInventory(String productType) {
        //  get inventory from scm
        return invSession.getRealInventory(productType);
    }

    public void simulateSO(ActionEvent event) {
        ArrayList<ArrayList<Long>> differences = returnDifferences();
        if (differences == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You cannot decrease production demands."));
            return;
        } else {
            String result = checkCapacity();
            if (result != null) {
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "Your capacity should not exceed 90%."
                        + "Please check " + result));
                return;
            }
            if (!atpSession.checkRawMaterial(differences, changedDates)){
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You don't have enough raw material. Please simulate again."));
                return;
            }
            indicativeLeadtime = atpSession.simulateSo(dailyDemands, otDailyDemands, differences, changedDates, pendingATP);
        }
        if (indicativeLeadtime == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_WARN, "Unsuccessful", "Your simulated daily demands are still not able to meet the requests."));
        } else {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_INFO, "Successful", "Simulation Done"));
        }
//        FacesContext.getCurrentInstance().addMessage(null,
//                new FacesMessage(FacesMessage.SEVERITY_INFO, "Successful", "Simulation Done"));
    }

    public void resetFormData(ActionEvent event) {
        for (int i = 0; i < 4; i++) {
            if (getChangedDates() != null) {
                for (int j = 0; j < getChangedDates().size(); j++) {
                    otDailyDemands.get(i).get(j).setL(getOrigOtDailyDemands().get(i).get(j));
                }
            }
        }
        for (int i = 0; i < 4; i++) {
            if (getChangedDates() != null) {
                for (int j = 0; j < getChangedDates().size(); j++) {
                    dailyDemands.get(i).get(j).setL(getOrigDailyDemands().get(i).get(j));
                }
            }
        }
    }

    public void simulateRFP(ActionEvent event) {
        ArrayList<ArrayList<Long>> differences = returnDifferences();
        if (differences == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You cannot decrease production demands."));
            return;
        } else {
            String result = checkCapacity();
            if (result != null) {
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "Your capacity should not exceed 90%."
                        + "Please check " + result));
                return;
            }
            if (!atpSession.checkRawMaterial(differences, changedDates)){
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You don't have enough raw material. Please simulate again."));
                return;
            }
            indicativeLeadtime = atpSession.simulateUrgentRfq(dailyDemands, otDailyDemands, differences, changedDates, pendingATP);
        }
        if (indicativeLeadtime == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_WARN, "Unsuccessful", "Your simulated daily demands are still not able to meet the requests."));
        } else {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_INFO, "Successful", "Simulation Done"));
        }
    }

    public String confirmRFQATPChecking() {
        ArrayList<ArrayList<Long>> differences = returnDifferences();
        if (differences == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You cannot decrease production demands."));
            return null;
        } else {
            String result = checkCapacity();
            if (result != null) {
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "Your capacity should not exceed 90%."
                        + "Please check " + result));
                return null;
            }
            if (!atpSession.checkRawMaterial(differences, changedDates)){
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You don't have enough raw material. Please simulate again."));
                return null;
            }
            indicativeLeadtime = atpSession.simulateUrgentRfq(dailyDemands, otDailyDemands, differences, changedDates, pendingATP);
            if (indicativeLeadtime == null) {
                // sq atp fail
                if (atpSession.changeDOStatusAfterATPFailedRFQ(pendingATP.getDeliveryOrder().getId())) {
                    atpSession.removePendingATP(pendingATP);
                    jobManager.assignNewJob(Consts.JOB_TYPE_ATP_RFQ_FAIL, pendingATP.getDeliveryOrder().getSalesQuotation().getId());
                } else {
                    FacesContext.getCurrentInstance().addMessage(null,
                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "update failed."));
                    return null;
                }
            }
            //TODO: rfq: change deo status, create job list for sales staff
            Long deoId = pendingATP.getDeliveryOrder().getId();
            if (atpSession.changeSQIndicativeLeadTime(deoId, pendingATP.getRequestedTime())) {
                atpSession.removePendingATP(pendingATP);
                jobManager.assignNewJob(Consts.JOB_TYPE_ATP_SQ_URGENT, deoId);
                return getRedirectPath(Consts.SIMULATE_FOR_RFQ_PATH + "Confirm");
            } else {
//                if (atpSession.changeDOStatusAfterATPFailedRFQ(pendingATP.getDeliveryOrder().getId())) {
//                    jobManager.assignNewJob(Consts.JOB_TYPE_ATP_RFQ_FAIL, pendingATP.getDeliveryOrder().getSalesQuotation().getId());
//                } else {
//                    FacesContext.getCurrentInstance().addMessage(null,
//                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "update failed."));
//                    return null;
//                }
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "Update failed."));
                return null;
            }
        }
    }

    public String confirmSOATPChecking() {
        ArrayList<ArrayList<Long>> differences = returnDifferences();
        if (differences == null) {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You cannot decrease production demands."));
            return null;
        } else {
            String result = checkCapacity();
            if (result != null) {
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "Your capacity should not exceed 90%."
                        + "Please check " + result));
                return null;
            }
            if (!atpSession.checkRawMaterial(differences, changedDates)){
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "You don't have enough raw material. Please simulate again."));
                return null;
            }
            if (atpSession.confirmSimulationOfSalesOrder(dailyDemands, otDailyDemands, differences, changedDates, pendingATP)) {
                //TODO: so: change deo status, create job list for sales staff, deliveryorder's 
                Long soId = pendingATP.getDeliveryOrder().getSalesOrder().getId();
                if (atpSession.changeDOStatusAfterConfirmation(soId, pendingATP.getRequestedTime(), pendingATP.getDeliveryOrder().getId())) {
                    jobManager.assignNewJob(Consts.JOB_TYPE_ATP_SO, soId);
                    atpSession.removePendingATP(pendingATP);
                    return getRedirectPath(Consts.SIMULATE_FOR_SO_PATH + "Confirm");
                } else {
                    FacesContext.getCurrentInstance().addMessage(null,
                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "update failed."));
                    return null;
                }
            } else {
                // TODO:
                if (atpSession.changeDOStatusAfterATPFailedSO(pendingATP.getDeliveryOrder().getId())) {
                    jobManager.assignNewJob(Consts.JOB_TYPE_ATP_DO_FAIL, pendingATP.getDeliveryOrder().getId());
                    atpSession.removePendingATP(pendingATP);
                    return getRedirectPath(Consts.SIMULATE_FOR_SO_PATH + "Confirm");
                } else {
                    FacesContext.getCurrentInstance().addMessage(null,
                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "update failed."));
                    return null;
                }
//                FacesContext.getCurrentInstance().addMessage(null,
//                        new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful", "Your confirmation failed. Please do simulation again."));
//                return null;
            }
        }
    }

    private String checkCapacity() {
        for (int j = 0; j < changedDates.size(); j++) {
            double total1 = 0d, total2 = 0d;
            for (int i = 0; i < 4; i++) {
                if (i == 0 || i == 1) {
                    total1 += dailyDemands.get(i).get(j).getL() * 1.0 / Consts.CASES_PER_HOUR_CLASSIC;
                    total2 += otDailyDemands.get(i).get(j).getL() * 1.0 / Consts.CASES_PER_HOUR_CLASSIC;
                } else {
                    total1 += dailyDemands.get(i).get(j).getL() * 1.0 / Consts.CASES_PER_HOUR_PREMIUM;
                    total2 += otDailyDemands.get(i).get(j).getL() * 1.0 / Consts.CASES_PER_HOUR_PREMIUM;
                }
            }
            if (total1 / Consts.NORMAL_SHIFT_PER_DAY > 0.9 || total2 / Consts.OVERTIME_EXTRA_SHIFT_PER_DAY > 0.9) {
                return " date " + Consts.sdfDmy.format(changedDates.get(j).getTime());
            }
        }
        return null;
    }

    // check whether it is only increasing without decreasing, check capacity
    private ArrayList<ArrayList<Long>> returnDifferences() {
        ArrayList<ArrayList<Long>> differences = new ArrayList<ArrayList<Long>>();
        for (int i = 0; i < 4; i++) {
            ArrayList<Long> temp = new ArrayList<Long>();
            for (int j = 0; j < changedDates.size(); j++) {
                Long diff = origDailyDemands.get(i).get(j) + origOtDailyDemands.get(i).get(j)
                        - dailyDemands.get(i).get(j).getL() - otDailyDemands.get(i).get(j).getL();
                if (diff > 0) {
                    return null;
                }
                temp.add(-diff);
            }
            differences.add(temp);
        }
        return differences;
    }

    public PendingATP getPendingATP() {
        PendingATP p1 = (PendingATP) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(ATPSelectManagerBean.SELECTED_ATP_RFQ);
        PendingATP p2 = (PendingATP) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(ATPSelectManagerBean.SELECTED_ATP_SO);
        if (p1 != null) {
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(ATPSelectManagerBean.SELECTED_ATP_RFQ);
            pendingATP = p1;
        }
        if (p2 != null) {
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(ATPSelectManagerBean.SELECTED_ATP_SO);
            pendingATP = p2;
        }
        return pendingATP;
    }

    public void setPendingATP(PendingATP pendingATP) {
        this.pendingATP = pendingATP;
    }

    public PendingATP getPendingRFQ() {
        if (pendingRFQ == null) {
            PendingATP temp = (PendingATP) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(ATPSelectManagerBean.SELECTED_ATP_RFQ);
            if (temp != null) {
                pendingRFQ = temp;
                pendingATP = temp;
                FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(ATPSelectManagerBean.SELECTED_ATP_SO);
            }
        }
        return pendingRFQ;
    }

    public void setPendingRFQ(PendingATP pendingRFQ) {
        this.pendingRFQ = pendingRFQ;
    }

    public PendingATP getPendingSO() {
        if (pendingSO == null) {
            PendingATP temp = (PendingATP) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(ATPSelectManagerBean.SELECTED_ATP_SO);
            if (temp != null) {
                pendingSO = temp;
                pendingATP = temp;
                FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(ATPSelectManagerBean.SELECTED_ATP_RFQ);
            }
        }
        return pendingSO;
    }

    public void setPendingSO(PendingATP pendingSO) {
        this.pendingSO = pendingSO;
    }

    public Calendar getRequestedDate() {
        // get requested date from somewhere
//        requestedDate = Calendar.getInstance();
//        requestedDate.set(2012, 0, 7);
        int month = Integer.valueOf(Consts.sdfMonth.format(getPendingATP().getRequestedTime()));
        int year = Integer.valueOf(Consts.sdfYear.format(getPendingATP().getRequestedTime()));
        int day = Integer.valueOf(Consts.sdfDay.format(getPendingATP().getRequestedTime()));
        requestedDate = Calendar.getInstance();
        requestedDate.set(year, month - 1, day, 0, 0, 0);
        return requestedDate;
    }

    public void setRequestedDate(Calendar requestedDate) {
        this.requestedDate = requestedDate;
    }

    public Calendar getRequestedDateSO() {
        //TODO: get requested date from somewhere?
        int month = Integer.valueOf(Consts.sdfMonth.format(getPendingATP().getRequestedTime()));
        int year = Integer.valueOf(Consts.sdfYear.format(getPendingATP().getRequestedTime()));
        int day = Integer.valueOf(Consts.sdfDay.format(getPendingATP().getRequestedTime()));
        requestedDateSO = Calendar.getInstance();
        requestedDateSO.set(year, month - 1, day, 0, 0, 0);
        return requestedDateSO;
    }

    public void setRequestedDateSO(Calendar requestedDateSO) {
        this.requestedDateSO = requestedDateSO;
    }

    public ArrayList<Calendar> getChangedDates() {
        if (changedDates == null) {
            changedDates = new ArrayList<Calendar>();

            if (getRequestedDate() != null) {
                long millis1 = requestedDate.getTimeInMillis();
                Calendar currentDate = Calendar.getInstance();
                currentDate.set(currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
                long millis2 = currentDate.getTimeInMillis(); // current time
                long d = (millis1 - millis2) / (24 * 60 * 60 * 1000);
                if ((millis1 - millis2) % (24 * 60 * 1000) == 0) {
                    d--;
                }
                if (d >= 0) {
                    for (int i = 0; i < d; i++) {
                        currentDate.add(Calendar.DAY_OF_MONTH, 1);
                        if (atpSession.checkIsWorkingDay(currentDate)) {
                            Calendar newC = Calendar.getInstance();
                            newC.setTime(currentDate.getTime());
                            changedDates.add(newC);
                        }
                    }
                }
            }
        }
        return changedDates;
    }

    public ArrayList<ArrayList<LongHolder>> getDailyDemands() {
        if (dailyDemands == null) {
            dailyDemands = new ArrayList<ArrayList<LongHolder>>();
            for (int i = 0; i < 4; i++) {
                ArrayList<LongHolder> temp = new ArrayList<LongHolder>();
                if (getChangedDates() != null) {
                    for (int j = 0; j < getChangedDates().size(); j++) {
                        temp.add(new LongHolder(getOrigDailyDemands().get(i).get(j)));
                    }
                }
                dailyDemands.add(temp);
            }
        }
        return dailyDemands;
    }

    public ArrayList<Integer> getNumOfDays() {
        if (numOfDays == null) {
            numOfDays = new ArrayList<Integer>();
            if (getChangedDates() != null) {
                for (int i = 0; i < getChangedDates().size(); i++) {
                    numOfDays.add(i);
                }
            }
        }
        //System.out.println("testing ::::::::::::" + numOfDays.size());
        return numOfDays;
    }

    public ArrayList<ArrayList<Long>> getOrigDailyDemands() {
        if (origDailyDemands == null) {
            origDailyDemands = new ArrayList<ArrayList<Long>>();
            //System.out.println("testing::::::::: getchangeddates size " + getChangedDates().size());
            if (getChangedDates() != null && !getChangedDates().isEmpty()) {
                ArrayList<Timestamp> tss = new ArrayList<Timestamp>();
                for (int i = 0; i < changedDates.size(); i++) {
                    tss.add(new Timestamp(changedDates.get(i).getTimeInMillis()));
                }
                origDailyDemands = atpSession.getDailyDemands(tss);
                //TODO: testing
//                System.out.println("testing for get daily demands");
//                for (Timestamp t: tss) {
//                    System.out.println("tesing:::::::::::: time: " + Consts.sdfDmy.format(t));
//                }
//                for (int t = 0; t < 4; t++ ) {
//                    System.out.println("tesing :::::::::::::" +origDailyDemands.get(t).size());;
//                }
            } else {
                for (int i = 0; i < 4; i++) {
                    ArrayList<Long> temp = new ArrayList<Long>();
                    origDailyDemands.add(temp);
                }
            }
        }
        return origDailyDemands;
    }

    public ArrayList<ArrayList<Long>> getOrigOtDailyDemands() {
        if (origOtDailyDemands == null) {
            origOtDailyDemands = new ArrayList<ArrayList<Long>>();
            if (getChangedDates() != null && !getChangedDates().isEmpty()) {
                ArrayList<Timestamp> tss = new ArrayList<Timestamp>();
                for (int i = 0; i < changedDates.size(); i++) {
                    tss.add(new Timestamp(changedDates.get(i).getTimeInMillis()));
                }
                origOtDailyDemands = atpSession.getDailyOTDemands(tss);
            } else {
                for (int i = 0; i < 4; i++) {
                    ArrayList<Long> temp = new ArrayList<Long>();
                    origOtDailyDemands.add(temp);
                }
            }
        }
        return origOtDailyDemands;
    }

    public ArrayList<ArrayList<LongHolder>> getOtDailyDemands() {
        if (otDailyDemands == null) {
            otDailyDemands = new ArrayList<ArrayList<LongHolder>>();
            for (int i = 0; i < 4; i++) {
                ArrayList<LongHolder> temp = new ArrayList<LongHolder>();
                if (getChangedDates() != null) {
                    for (int j = 0; j < getChangedDates().size(); j++) {
                        temp.add(new LongHolder(getOrigOtDailyDemands().get(i).get(j)));
                    }
                }
                otDailyDemands.add(temp);
            }
        }
        return otDailyDemands;
    }

    // for general purpose
    public String returnCalendarString(Calendar c) {
        return Consts.sdfDmy.format(new Timestamp(c.getTimeInMillis()));
    }

    public ArrayList<Integer> getCounterForProduct() {
        return counterForProduct;
    }

    public ArrayList<Integer> getCounterForWeek() {
        return counterForWeek;
    }

    private Long roundToLong(Double d) {
        return Math.round(d);
    }

    private String getRedirectPath(String path) {
        return path + "?faces-redirect=true";
    }

    public String returnProductAlpha(Integer i) {
        if (i.intValue() == 0) {
            return "A";
        } else if (i.intValue() == 1) {
            return "B";
        } else if (i.intValue() == 2) {
            return "C";
        } else if (i.intValue() == 3) {
            return "D";
        } else {
            return "";
        }
    }

    public String returnProductName(Integer i) {
        if (i.intValue() == 0) {
            return Consts.ProductType.BAR_A.getText();
        } else if (i.intValue() == 1) {
            return Consts.ProductType.BAR_B.getText();
        } else if (i.intValue() == 2) {
            return Consts.ProductType.BAR_C.getText();
        } else if (i.intValue() == 3) {
            return Consts.ProductType.BAR_D.getText();
        } else {
            return "";
        }
    }

    public String returnFormattedDay(Timestamp t) {
        if (t == null) {
            return "";
        }
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM");
        return sdf.format(t);
    }

    public JobManagerBean getJobManager() {
        return jobManager;
    }

    public void setJobManager(JobManagerBean jobManager) {
        this.jobManager = jobManager;
    }
}
